import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @author xucaiqin
 * 排序工具
 */
@Slf4j
public class OrderUtil {
	private static final char UNDERLINE = '_';
	private static final String ONE = "1";
	private static final String ASC = " asc";
	private static final String DESC = " desc";
	//不使用convert函数进行排序的字段
	private static final String DECIMAL = "class java.math.BigDecimal";
	private static final String TIME = "class java.time.LocalDateTime";
	private static final String FLOAT = "class java.lang.Float";

	/**
	 * 驼峰格式字符串转下划线格式字符串(驼峰转下划线)
	 *
	 * @param sortField 驼峰命名字符串(排序字段)：createTime
	 * @return
	 */
	public static String sortFieldToUnderCase(String sortField) {
		int len = sortField.length();
		StringBuilder sb = new StringBuilder(len);
		for (int i = 0; i < len; i++) {
			char c = sortField.charAt(i);
			if (Character.isUpperCase(c)) {
				sb.append(UNDERLINE);
				sb.append(Character.toLowerCase(c));
			} else {
				sb.append(c);
			}
		}
		return sb.toString();
	}

	/**
	 * 验证字段:是否使用convert
	 * 是否是BigDecimal等其它可以不使用convert函数进行排序的字段
	 *
	 * @param sortField
	 * @return true:不使用convert函数 false:使用convert函数 null:不是当前实体（表）的字段
	 */
	private static Boolean dontUseConvert(String sortField, final Class<?> entityClass) {
		Boolean res = null;
		Field field;
		if (StringUtils.isBlank(sortField)) {
			field = FieldUtils.getDeclaredField(entityClass, "createTime", true);
		} else {
			field = FieldUtils.getDeclaredField(entityClass, sortField, true);
		}
		if (field == null) {
			return res;
		} else {
			String s = field.getType().toString();
			//可以加上更多需要验证的字段:不使用convert函数进行排序
			switch (s) {
				case DECIMAL:
				case TIME:
				case FLOAT:
					res = true;
					break;
				default:
					res = false;
			}
		}
		return res;
	}

	/**
	 * 拼接排序语句:带有convert函数
	 *
	 * @param prefix    驼峰命名字符串(排序字段)：createTime
	 * @param sortField 驼峰命名字符串(排序字段)：createTime
	 * @param sortType  排序类型：0 desc 降序, 1 asc 升序
	 * @return
	 */
	private static String splicingSortSentence(String prefix, String sortField, String sortType) {
		return "order by CONVERT(" + prefix + sortFieldToUnderCase(sortField) + " USING gbk)" + (ONE.equals(sortType) ? ASC : DESC);
	}

	/**
	 * 拼接排序语句:不带有convert函数
	 *
	 * @param prefix    驼峰命名字符串(排序字段)：createTime
	 * @param sortField 驼峰命名字符串(排序字段)：createTime
	 * @param sortType  排序类型：0 desc 降序, 1 asc 升序
	 * @return
	 */
	private static String splicingSortSentenceNone(String prefix, String sortField, String sortType) {
		return "order by " + prefix + sortFieldToUnderCase(sortField) + (ONE.equals(sortType) ? ASC : DESC);
	}

	/**
	 * 驼峰转下滑线
	 * and 拼接convert函数
	 *
	 * @param sortField 驼峰命名字符串(排序字段)：createTime
	 * @return
	 */
	private static String changeAndSplice(String sortField) {
		return " CONVERT(" + sortFieldToUnderCase(sortField) + " USING gbk) ";
	}

	/**
	 * 驼峰转下滑线
	 * and 拼接convert函数
	 * and 带有表前缀
	 *
	 * @param sortField 驼峰命名字符串(排序字段)：createTime
	 * @return
	 */
	private static String changeAndSplice(String prefix, String sortField) {
		return " CONVERT(" + prefix + sortFieldToUnderCase(sortField) + " USING gbk) ";
	}

	/**
	 * 驼峰转下滑线
	 * and 拼接convert函数
	 * and 带有表前缀
	 *
	 * @param sortField 驼峰命名字符串(排序字段)：createTime
	 * @return
	 */
	private static String changeAndSpliceNone(String prefix, String sortField) {
		return prefix + sortFieldToUnderCase(sortField);
	}

	/**
	 * 单表排序,使用Wrapper,带验证BigDecimal
	 *
	 * @param sortField 驼峰命名字符串(排序字段) ：createTime
	 * @param sortType  排序类型：0 desc 降序, 1 asc 升序
	 * @param wrapper   wrapper
	 * @return
	 */
	public static void singleTableSort(String sortField, String sortType, final Class<?> entityClass, AbstractWrapper wrapper) {
		//验证字段是否不使用convert函数
		Boolean aBoolean = dontUseConvert(sortField, entityClass);
		if (aBoolean == null) {
			wrapper.orderBy(true, false, sortFieldToUnderCase(sortField));
		} else {
			if (aBoolean) {
				wrapper.orderBy(true, ONE.equals(sortType), sortFieldToUnderCase(sortField));
			} else {
				wrapper.orderBy(true, ONE.equals(sortType), changeAndSplice(sortField));
			}
		}
	}

	/**
	 * 多表排序：双表 使用wrapper
	 *
	 * @param sortAndPrefix 排序字段和表前缀 0:sortField,1:sortType,2:prefixPrimaryTable,3:prefixFromTable
	 * @param primaryTable  【主表】的实体class
	 * @param fromTable     【从表】的实体class
	 * @param wrapper       条件构造器
	 * @return
	 */
	public static void multiTableSort(List<String> sortAndPrefix, final Class<?> primaryTable, final Class<?> fromTable, AbstractWrapper wrapper) {
		if (sortAndPrefix.size() != 4) {
			return;
		}
		String sortField = sortAndPrefix.get(0);
		String sortType = sortAndPrefix.get(1);
		String prefixPrimaryTable = sortAndPrefix.get(2);
		String prefixFromTable = sortAndPrefix.get(3);
		//主表验证
		Boolean aBoolean = dontUseConvert(sortField, primaryTable);
		if (aBoolean == null) {
			//从表验证
			Boolean bBoolean = dontUseConvert(sortField, fromTable);
			if (bBoolean == null) {
				//即非主表也非从表
				wrapper.orderBy(true, false, changeAndSplice(prefixPrimaryTable, "createTime"));
			} else {
				//从表
				if (bBoolean) {
					wrapper.orderBy(true, ONE.equals(sortType), changeAndSpliceNone(prefixPrimaryTable, sortField));
				} else {
					wrapper.orderBy(true, ONE.equals(sortType), changeAndSplice(prefixFromTable, sortField));
				}
			}
		} else {
			//主表
			if (aBoolean) {
				wrapper.orderBy(true, ONE.equals(sortType), changeAndSpliceNone(prefixPrimaryTable, sortField));
			} else {
				wrapper.orderBy(true, ONE.equals(sortType), changeAndSplice(prefixFromTable, sortField));
			}
		}
	}

	/**
	 * 多表排序：双表
	 *
	 * @param sortField    驼峰命名字符串(排序字段)：createTime
	 * @param sortType     排序类型：0 desc 降序, 1 asc 升序
	 * @param prefixTwo    两张表的表前缀（表别名em："xxx."） index=0,主表，index=1,从表，（当排序字段既非主表也非从表时：index=2 表别名的前缀，index=3 排序字段）
	 * @param primaryTable 【主表】的实体class
	 * @param fromTable    【从表】的实体class
	 * @return
	 */
	public static String multiTableSort(String sortField, String sortType, List<String> prefixTwo, final Class<?> primaryTable, final Class<?> fromTable) {
		ArrayList<Class<?>> classes = new ArrayList<>(2);
		classes.add(primaryTable);
		classes.add(fromTable);
		return multiTableSort(sortField, sortType, prefixTwo, classes);
	}

	/**
	 * 多表排序：双表即以上
	 *
	 * @param sortField  驼峰命名字符串(排序字段)：createTime
	 * @param sortType   排序类型：0 desc 降序, 1 asc 升序
	 * @param prefixList 多张表的表前缀（表别名） 表前缀的索引需要与实体的索引对应
	 * @param multiTable 多表的实体class数组list  表前缀的索引需要与实体的索引对应
	 * @return
	 */
	public static String multiTableSort(String sortField, String sortType, List<String> prefixList, List<Class<?>> multiTable) {
		String res = "";
		for (int i = 0; i < multiTable.size(); i++) {
			Boolean aBoolean = dontUseConvert(sortField, multiTable.get(i));
			if (aBoolean == null) {
				continue;
			} else {
				if (aBoolean == true) {
					res = splicingSortSentenceNone(prefixList.get(i), sortField, sortType);
					return res;
				} else {
					res = splicingSortSentence(prefixList.get(i), sortField, sortType);
					return res;
				}
			}
		}
		if (prefixList.size() == 4) {
			res = "order by CONVERT(" + prefixList.get(2) + sortFieldToUnderCase(prefixList.get(3)) + " USING gbk)" + (ONE.equals(sortType) ? ASC : DESC);
		}
		return res;
	}
}
